﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Futaba.SBus.Readers
{
	/// <summary>
	/// An instance describes an available COM port
	/// </summary>
	public class ComPortInformation : NotifyPropertyChangedBase
	{

		/// <summary>
		/// Creates a default instance
		/// </summary>
		public ComPortInformation()
		{
		}
		/// <summary>
		/// Creates an instance with the supplied values
		/// </summary>
		/// <param name="portId">the value for the <see cref="PortId"/></param>
		/// <param name="displayName">the value for the <see cref="DisplayName"/></param>
		public ComPortInformation(string portId, string displayName)
		{
			PortId = portId;
			DisplayName = displayName;
		}

		private string _internalDisplayName;
		/// <summary>
		/// A more concret name of this port (discovered via WMI)
		/// </summary>
		public string DisplayName
		{
			get { return _internalDisplayName; }
			set
			{
				if (Equals(_internalDisplayName, value))
					return;
				_internalDisplayName = value;
				RaisePropertyChanged("DisplayName");
			}
		}

		private string _internalPortId;
		/// <summary>
		/// The system-name of the port this instance resembles
		/// </summary>
		public string PortId
		{
			get { return _internalPortId; }
			set
			{
				if (Equals(_internalPortId, value))
					return;
				_internalPortId = value;
				RaisePropertyChanged("PortId");
			}
		}

		/// <summary>
		/// Returns a list of available COM ports on this system. As it makes WMI calls this is done
		/// asynchronously
		/// </summary>
		/// <returns></returns>
		public static async Task<List<ComPortInformation>> GetAvailableComPorts()
		{
			//var ports = SerialPort.GetPortNames();
			var ports = await GetComPortDescriptions();
			var list = new List<ComPortInformation>(ports.Count);
			foreach (var port in ports)
			{
				var model = new ComPortInformation(port.Key, port.Value);
				list.Add(model);
			}
			return list.OrderBy(p => p.DisplayName).ToList();
		}

		private static readonly Regex ComPortRegex = new Regex(@"(?in)\((?<com>COM\d+)\)");
		private static async Task<Dictionary<string, string>> GetComPortDescriptions()
		{
			var dict = new Dictionary<string, string>(20);

			await Task.Run(() =>
			{
				var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE Caption like '%(COM%'");
				foreach (ManagementObject queryObj in searcher.Get())
				{
					var desc = (string)queryObj["Caption"];
					var match = ComPortRegex.Match(desc);
					if (match.Success)
					{
						dict.Add(match.Groups["com"].Value, desc);
					}
				}
			});
			return dict;
		}

		/// <summary>
		/// Returns the <see cref="DisplayName"/> of this instance (fallback is the <see cref="PortId"/>)
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return DisplayName ?? PortId;
		}

	}
}
